import java.io.*;
import java.util.*;
import java.net.*;

public class Player {

	private String _name;
	private int _gsIndx;
	private boolean _finished = false;
	private String _addr;
	private int _port;
	private int _turnCount = 0;
	private boolean _verbose = false;

	private ServerSocket _serverSocket = null;
	private Socket _clientSocket = null;
	private BufferedOutputStream _bos = null;
	private BufferedInputStream _bis = null;
	private byte [] _msgBuffer = new byte[1024];

	private final int FIRST = 0;
	private final int SECOND = 1;
	private final int SCORING = 2;


	public Player(String name, String addr, int port, int gsIndx, int totalPlayers) {
		_name = name;
		_addr = addr;
		_port = port;
		_gsIndx = gsIndx;
		
		try {
			_serverSocket = new ServerSocket(port);
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			_clientSocket = _serverSocket.accept();
			_bos = new BufferedOutputStream(_clientSocket.getOutputStream());
			_bis = new BufferedInputStream(_clientSocket.getInputStream());
			//let the agent know his 
			int index = ByteManipulator.addInt(_msgBuffer, _gsIndx, 0, false);
			index = ByteManipulator.addInt(_msgBuffer, totalPlayers, index, false);
			_bos.write(_msgBuffer,0,8);
			_bos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	public Player(String name, String addr, int port, int gsIndx, int totalPlayers, boolean verbose) {
		this(name,addr,port,gsIndx,totalPlayers);
		_verbose = true;
	}

	public Player() {
		//Dummy agent used for computations
	}

	public void reset() {
		_turnCount = 0;
		_finished = false;
	}

	public void shutDown() {
		try {
			if (_verbose) System.out.println(_name + " is shutting down.");
			int indx = ByteManipulator.addInt(_msgBuffer,99,0,false);
			_bos.write(_msgBuffer,0,4);
			_bos.flush();
			//wait for acknowledgement (can be anything);
			//_bis.read(_msgBuffer,0,1024);

			_bis.close();
			_bos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void getHold(DiceRoll dr, int msgType, GameState gs) {
		int index = ByteManipulator.addInt(_msgBuffer,msgType,0,false);
		index = ByteManipulator.addIntArray(_msgBuffer,dr._dice,index,0,5,false);
		if (msgType == 0) {
			index = ByteManipulator.addIntArray(_msgBuffer,gs.getIntArray(),index,0,gs.getSize(),false);
		}
		try {
			_bos.write(_msgBuffer,0,index);
			_bos.flush();
			int msgSize = _bis.read(_msgBuffer,0,1024);
			int holdPattern = ByteManipulator.getInt(_msgBuffer,0,false);
			//System.out.println("Received pattern: " + holdPattern);
			processPattern(holdPattern,dr);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	int getScoring(DiceRoll dr) {
		int index = ByteManipulator.addInt(_msgBuffer,SCORING,0,false);
		index = ByteManipulator.addIntArray(_msgBuffer,dr._dice,index,0,5,false);
		try {
			_bos.write(_msgBuffer,0,index);
			_bos.flush();
			int msgSize = _bis.read(_msgBuffer,0,1024);
			int holdPattern = ByteManipulator.getInt(_msgBuffer,0,false);
			//System.out.println("Received pattern: " + holdPattern);
			return holdPattern;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return -1;
	}

	public void takeTurn(GameState gs) {
		if (_finished) return;
		_turnCount++;
		if (_verbose) System.out.println(_name + "(" + _gsIndx + ") is taking turn #" + _turnCount);

		DiceRoll dr = new DiceRoll();
		if (_verbose)	System.out.println(dr);
		
		getHold(dr,0,gs);
		dr.rollAgain();
		if (_verbose)	System.out.println(dr);

		getHold(dr,1,null);
		dr.rollAgain();
		if (_verbose)	System.out.println(dr);
		
		int scoring = getScoring(dr);
		int score = scoreRoll(gs,dr,scoring);

		if (_turnCount >= 13) _finished = true;
	}

	public void processPattern(int holdPattern, DiceRoll dr) {
		dr.freeAll();
		if ( (holdPattern & 0x01) == 0x01) dr.hold(0);
		if ( (holdPattern & 0x02) == 0x02) dr.hold(1);
		if ( (holdPattern & 0x04) == 0x04) dr.hold(2);
		if ( (holdPattern & 0x08) == 0x08) dr.hold(3);
		if ( (holdPattern & 0x10) == 0x10) dr.hold(4);
	}


	public String getName() {
		return _name;
	}

	public int getIndx() {
		return _gsIndx;
	}

	public boolean finished() {
		return _finished;
	}

	public int scoreRoll(GameState gs, DiceRoll dr, int category) {
		return scoreRoll(gs, dr, category, false);
	}

	public int scoreRoll(GameState gs, DiceRoll dr, int category, boolean overide) {

		if (gs.getData(_gsIndx, category) >= 0 && !overide) {
			if (_verbose) System.out.println(" ERROR: Category(" + category + ") choice.  Using first available");
			for (int i=GameState.ONES; i<=GameState.CHANCE; i++) {
				if (gs.getData(_gsIndx,i) < 0) {
					category = i;
					break;	
				}
			}
		}
		
		int score = 0;
		if (category <= GameState.SIXES) {
			for (int i=0; i<5; i++) {
				score += dr._dice[i] * (dr._dice[i] == category ? 1 : 0);
			}
		}

		switch (category) {
			case GameState.THREE_OF_A_KIND: score = score3K(dr); break;
			case GameState.FOUR_OF_A_KIND: score = score4K(dr); break;
			case GameState.FULL_HOUSE: score = scoreFH(dr); break;
			case GameState.SMALL_STRAIGHT: score = scoreSS(dr); break;
			case GameState.LARGE_STRAIGHT: score = scoreLS(dr); break;
			case GameState.YAHTZEE: score = scoreY(dr); break;
			case GameState.CHANCE: score = scoreC(dr); break;
			default: break;
		}

		gs.setData(_gsIndx, category, score);
		//System.out.println(" " + GameState.hr(category) + ": " + score + (category > GameState.SIXES ? " <" + dr + ">" : ""));

		return score;
	}

	public int score3K(DiceRoll dr) {
		int [] count = new int[6];
		int total = 0;
		for (int d : dr._dice) {
			count[d-1]++;
			total += d;
		}
		for (int c : count) {
			if (c >= 3) {
				return total;
			}
		}
		return 0;
	}

	public int score4K(DiceRoll dr) {
		int [] count = new int[6];
		int total = 0;
		for (int d : dr._dice) {
			count[d-1]++;
			total += d;
		}
		for (int c : count) {
			if (c >= 4) {
				return total;
			}
		}
		return 0;
	}

	public int scoreFH(DiceRoll dr) {
		int [] count = new int[6];
		for (int d : dr._dice) {
			count[d-1]++;
		}

		boolean two = false;
    boolean three = false;
		for	 (int c : count) {
			//System.out.println(" c = " + c);
			if (c == 2) {
				two = true;
			}
			if (c == 3) {
				three = true;
			}
			if (c == 5) {
				return 25;
			}
			if (two && three) {
				return 25;
			}
		}

		return 0;
	}

	public int scoreSS(DiceRoll dr) {
		int [] count = new int[6];
		
		for (int d : dr._dice) {
			count[d-1]++;
		}

		int start = -1;
		for (int i=0; i<6; i++) {
			 if (count[i] > 0) {
					if (start < 0) {
//						System.out.println("" + i + " - restart");
						start = i;
					} else if (count[i-1] > 0 && (i-start) >= 3) {
//						System.out.println("" + i + " - finish");
						return 30;
					} else {
//						System.out.println("" + i + " - middle");
					}
			} else {
//				System.out.println("" + i + " - nothing here");
				start = -1;
			}
		}

		return 0;
	}

	public int scoreLS(DiceRoll dr) {
		int [] count = new int[6];
		
		for (int d : dr._dice) {
			count[d-1]++;
		}

		int start = -1;
		for (int i=0; i<6; i++) {
			 if (count[i] > 0) {
					if (start < 0) {
						start = i;
					} else if (count[i-1] > 0 && (i-start) >= 4) {
						return 40;
					}
			} else { 
				start = -1;
			}
		}

		return 0;
	}

	public int scoreY(DiceRoll dr) {
		if (
			dr._dice[0] == dr._dice[1] &&
			dr._dice[0] == dr._dice[2] &&
			dr._dice[0] == dr._dice[3] &&
			dr._dice[0] == dr._dice[4]) {
			return 50;
		}
		return 0;
	}

	public int scoreC(DiceRoll dr) {
		int score = 0;
		for (int d : dr._dice) {
			score += d;
		}

		return score;
	}







}
